#ifndef __testbed_H__
#define __testbed_H__

#ifdef PLATFORM_GLX

#include <GL/gl.h>
#include <GL/glx.h>

#else

#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
#include <EGL/egl.h>

#endif

#include <iostream>

namespace testbed
{

extern const char arg_prefix[];
extern const char arg_app[];

namespace util
{

struct pix
{
	GLubyte c[3];

	pix() {}

	pix(const GLubyte r,
		const GLubyte g,
		const GLubyte b)
	{
		c[0] = r;
		c[1] = g;
		c[2] = b;
	}
};

inline unsigned
next_multiple_of_pix_integral(
	const unsigned unaligned_size)
{
	// compute pix's optimal integral size
	unsigned integral_size = sizeof(pix);

	// isolate the leading bit
	while (integral_size & integral_size - 1)
		integral_size &= integral_size - 1;

	// double up if original quantity was not power of two
	if (sizeof(pix) & sizeof(pix) - 1)
		integral_size <<= 1;

	return (unaligned_size + integral_size - 1) & ~(integral_size - 1);
}

void
fill_with_checker(
	pix* const buffer,
	const unsigned stride,
	const unsigned dim_x,
	const unsigned dim_y,
	const unsigned granularity = 8);

bool
fill_from_file(
	pix* const buffer,
	const unsigned stride,
	const unsigned dim_x,
	const unsigned dim_y,
	const char* filename);

void
fill_YUV420_from_RGB(
	uint8_t* const y_buffer,
	uint8_t* const u_buffer,
	uint8_t* const v_buffer,
	const unsigned y_stride,
	const unsigned u_stride,
	const unsigned v_stride,
	pix* const rgb_buffer,
	const unsigned rgb_stride,
	const unsigned dim_x,
	const unsigned dim_y);

bool
setupTexture2D(
	const GLuint tex_name,
	const char* filename,
	const unsigned tex_w,
	const unsigned tex_h);

bool
setupTextureYUV420(
	const GLuint (&tex_name)[3],
	const char* filename,
	const unsigned tex_w,
	const unsigned tex_h);

bool
setupTexture2D(
	const GLuint tex_name,
	const pix* tex_src,
	const unsigned tex_w,
	const unsigned tex_h);

bool
setupShader(
	const GLuint shader_name,
	const char* source_filename);

bool
setupProgram(
	const GLuint prog_name,
	const GLuint shader_v_name,
	const GLuint shader_f_name);

bool
fill_indexed_mesh_from_file_PN(
	const char* filename,
	const GLuint vbo_arr,
	const GLuint vbo_idx,
	unsigned& num_faces,
	const bool is_rotated);

bool
fill_indexed_mesh_from_file_PN2(
	const char* filename,
	const GLuint vbo_arr,
	const GLuint vbo_idx,
	unsigned& num_faces,
	const bool is_rotated);

bool
reportGLError(
	std::ostream& stream = std::cerr);

#ifndef PLATFORM_GLX

bool
reportEGLError(
	std::ostream& stream = std::cerr);

#endif

} // namespace testbed::util

namespace hook
{

bool
init_resources(
	const unsigned argc,
	const char* const * argv);

bool
deinit_resources();

bool
render_frame();

bool
input_frame(
	const GLuint input);

enum {
	INPUT_MASK_ESC			= 1 << 0,
	INPUT_MASK_LEFT			= 1 << 1,
	INPUT_MASK_RIGHT		= 1 << 2,
	INPUT_MASK_DOWN			= 1 << 3,
	INPUT_MASK_UP			= 1 << 4,

	INPUT_MASK_FORCE_TYPE	= GLuint(-1)
};

bool
set_num_drawcalls(
	const unsigned n);

unsigned
get_num_drawcalls();

bool
requires_depth();

} // namespace testbed::hook

} // namespace testbed

#endif // __testbed_H__
